package com.nuctech.hardwaredriver.serialport_library.core;

import android.os.Looper;

import com.nuctech.hardwaredriver.serialport_library.bean.WriteSerialPortDataBean;
import com.nuctech.hardwaredriver.serialport_library.core.codec.SerialPortProtocolEncoder;
import com.nuctech.hardwaredriver.serialport_library.core.exception.SerialPortOnMainThreadException;
import com.nuctech.hardwaredriver.util.CloseUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

import android_serialport_api.SerialPort;

/**
 * Created by 心魂free on 2019/10/30.
 * <p>
 * 单利，串口管理类，负责保存串口会话及发送数据
 */

public class SerialPortManager {

    private SerialPort mSerialPort;
    private InputStream mInputStream;
    private OutputStream mOutputStream;
    private WaitSignalManager mWaitSignalManager;
    private int timeOut;
    private SerialPortReadThread readThread;
    private SerialPortProtocolEncoder protocolEncoder;

    private SerialPortManager() {
    }

    private static class SingletonHolder {

        private static final SerialPortManager INSTANCE = new SerialPortManager();
    }

    public static SerialPortManager getInstance() {

        return SingletonHolder.INSTANCE;
    }

    /**
     * 打开串口
     *
     * @param config 串口配置项
     */
    public void open(SerialPortConfig config) {

        try {

            mSerialPort = new SerialPort(new File(config.getSerialPortNumber()),
                    config.getBaudRate(), config.getStopBits(), config.getDataBits(),
                    config.getParity(), config.getFlowCon(), config.getFlags());

            mInputStream = mSerialPort.getInputStream();
            mOutputStream = mSerialPort.getOutputStream();

        } catch (IOException e) {

            e.printStackTrace();
        }

        timeOut = config.getTimeOut();
        protocolEncoder = config.getCodecFactory().getEncoder();

        mWaitSignalManager = WaitSignalManager.getInstance();

        //开启读取返回值线程
        readThread = new SerialPortReadThread(mInputStream, config.getCodecFactory().getDecoder(),
                mWaitSignalManager);
        readThread.setName("readSerialPortThread");
        readThread.setSerialPortHandler(config.getSerialPortHandler());
        readThread.start();
    }

    /**
     * 关闭串口
     */
    public void close() {

        if (readThread != null) {

            readThread.stopThread();
            readThread = null;
        }

        CloseUtil.closeQuietly(mInputStream);
        CloseUtil.closeQuietly(mOutputStream);

        if (mSerialPort != null) {

            mSerialPort.close();
            mSerialPort = null;
        }
    }

    /**
     * 向串口写入数据，必须在子线程工作
     *
     * @param writeSerialPortDataBean 待写入的数据
     * @return 当次通信是否正常
     */
    public boolean writeToSerialPort(WriteSerialPortDataBean writeSerialPortDataBean) {

        if (isMainThread()) {

            throw new SerialPortOnMainThreadException("串口发送数据在主线程!");
        }

        boolean isNormal = false;
        if (mOutputStream != null) {

            Lock lock = mWaitSignalManager.getLock();
            lock.lock();

            try {

                protocolEncoder.encode(mOutputStream, writeSerialPortDataBean);

                //唤醒接收线程
                mWaitSignalManager.getReadCondition().signal();
                //暂定指定时间（单位为:s）发送数据线程等待数据返回后唤醒继续执行
                isNormal = mWaitSignalManager.getWriteCondition()
                        .await(timeOut, TimeUnit.SECONDS);

            } catch (IOException | InterruptedException e) {

                e.printStackTrace();

            } finally {

                lock.unlock();
            }
        }

        return isNormal;
    }

    //检查是否为主线程
    private boolean isMainThread() {

        return Looper.getMainLooper().getThread() == Thread.currentThread();
    }
}
